﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LaoLuo.Threading
{
    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TIn"></typeparam>
    /// <typeparam name="TOut"> </typeparam>
    public class QueueHandler<TIn, TOut>
    {
        private readonly ConcurrentQueue<TIn> _queue;
        private readonly Func<TIn, TOut> _action;
        private int _queueCount;
        
        /// <summary>
        /// 
        /// </summary>
        public delegate void ItemEventHandler(TIn item, TOut result);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="num"> </param>
        public delegate void ItemQueueEventHandler(int num);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="action"></param>
        /// <param name="items"> </param>
        public QueueHandler(Func<TIn, TOut> action, params TIn[] items)
        {
            _action = action;
            _queue = new ConcurrentQueue<TIn>();
            InitQueue(items);
        }

        /// <summary>
        /// 
        /// </summary>
        public ItemEventHandler OnItemFinished { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public ItemQueueEventHandler OnFinished { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="items"></param>
        private void InitQueue(params TIn[] items)
        {
            if (_queueCount > 0)
            {
                throw new ApplicationException("queue is already inited");
            }
            foreach (var item in items)
            {
                _queue.Enqueue(item);
            }

            _queueCount = _queue.Count;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <exception cref="NotImplementedException"></exception>
        public void Start()
        {
            var working = Task.Factory.StartNew(() =>
                                      {
                                          while (_queue.Count > 0)
                                          {
                                              Act();
                                          }
                                      });

            working.ContinueWith(t => OnFinished(_queueCount));

        }

        private void Act()
        {
            TIn item;
            if (_queue.TryDequeue(out item))
            {
                var result = _action(item);
                OnItemFinished(item, result);
            }
        }
    }
}
